<template>
  <view
    style="visibility: hidden; transition-property: opacity, transform"
    :style="[{ transitionDuration: `${newDuration}ms`, transitionTimingFunction, transformOrigin, visibility }, newStyle, cusStyle]"
    :class="[cusClass]"
    @click="emits('click')"
  >
    <slot name="default"></slot>
  </view>
</template>

<script lang="ts" setup>
  import { type CSSProperties, type PropType, nextTick, onBeforeMount, onUnmounted, ref, watch } from 'vue';
  import type { $AnimationMode } from '../mxp-tool/types/interface';
  import { publicStyeProps } from '../mxp-tool/utils/public-props';
  import { getAmatons } from '../mxp-tool/utils/dom';
  defineOptions({
    name: 'mxp-translate',
  });

  const props = defineProps({
    /** 动画持续时间 */
    duration: { type: Number, default: 300 },
    /** 动画方式 */
    transitionTimingFunction: { type: String, default: 'ease-in-out' },
    /**
     * 过度动画类型。
     * @param fade - 渐隐渐出过渡
     * @param slide-top - 由上至下过渡
     * @param slide-right - 由右至左过渡
     * @param slide-bottom - 由下至上过渡
     * @param slide-left - 由左至右过渡
     * @param zoom-in - 由小到大过渡
     * @param zoom-out - 由大到小过渡
     * @param scale-bottom - 从上到下缩放
     * @param scale-top - 从下到上缩放
     * @param scale-bottom - 从右到左缩放
     * @param scale-top - 从左到右缩放
     */
    mode: { type: String as PropType<$AnimationMode>, default: 'fade' },
    /** 是否反向动画 */
    reverse: { type: Boolean, default: false },
    /** 动画的中心点 */
    transformOrigin: { type: String, default: 'center center' },
    /** 初始化后立即显示 */
    autoShow: { type: Boolean, default: false },
    /** 是否自动隐藏 */
    autoHide: { type: Boolean, default: false },
    /** 自动隐藏的延迟时间 */
    autoHideDelay: { type: Number, default: 2000 },
    ...publicStyeProps, // 混淆props共有参数
  });
  defineSlots<{
    /** 主体内容 */
    default: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'click'): void;
    (e: 'animationStart', type: 'hide-animation' | 'show-animation'): void;
    (e: 'animationEnd', type: 'hide-animation' | 'show-animation'): void;
    (e: 'update:modelValue', type: boolean): void;
    (e: 'change', type: boolean): void;
  }>();
  /** 自动隐藏的Timeout */
  const autoHideDelayTimeout = ref<NodeJS.Timeout | undefined>(undefined);
  /** 动画结束的Timeout */
  const animationTimeout = ref<NodeJS.Timeout | undefined>(undefined);

  /** 样式 */
  const newStyle = ref<CSSProperties>({});

  /** 是否正在过度 */
  const animationState = ref<boolean>(false);

  /** 正在过度的类型 */
  const animationType = ref<'hide-animation' | 'show-animation'>('hide-animation');

  watch(animationState, (state) => {
    emits('update:modelValue', state);
    emits('change', state);
  });
  const newDuration = ref(0);
  const visibility = ref<'hidden' | 'visible'>('hidden');
  newStyle.value = getAmatons(props.mode, props.reverse);
  onBeforeMount(() => {
    props.autoShow && show();
  });

  onUnmounted(() => {
    clearTimeoutFun();
  });

  /**
   * 自动隐藏
   */
  function autoHideDelayFun() {
    if (props.autoHide && props.autoHideDelay > 0) {
      clearTimeoutFun();
      autoHideDelayTimeout.value = setTimeout(() => {
        hide();
      }, props.autoHideDelay);
    }
  }

  /**
   * 清除 Timeou
   */
  function clearTimeoutFun() {
    clearTimeout(autoHideDelayTimeout.value);
    clearTimeout(animationTimeout.value);
  }

  /** 隐藏 (用于过度后还原，也会有动画效果) */
  function hide() {
    if (animationState.value && animationType.value === 'hide-animation') {
      // 正在过度中
    } else {
      clearTimeoutFun();
      emits('animationStart', 'hide-animation');
      animationState.value = true;
      animationType.value = 'hide-animation';

      newStyle.value = getAmatons(props.mode, props.reverse);
      animationTimeout.value = setTimeout(() => {
        animationState.value = false;
        emits('animationEnd', 'hide-animation');
      }, props.duration);
    }
  }

  /** 播放 */
  function show() {
    animationState.value = false;
    clearTimeoutFun();
    newDuration.value = props.duration;

    nextTick(() => {
      newStyle.value = getAmatons(props.mode, !props.reverse);
      visibility.value = 'visible';
      animationTimeout.value = setTimeout(() => {
        animationState.value = false;
        emits('animationEnd', 'show-animation');
        autoHideDelayFun();
      }, props.duration);
    });
  }

  defineExpose({
    /** 隐藏 (用于过度后还原，也会有动画效果) */
    hide,

    show,

    /** 得到是否正在过度 */
    getAnimationState() {
      return animationState.value;
    },
  });
</script>
